package com.catazy.cameralib

import android.content.Context
import android.graphics.Bitmap
import android.graphics.BitmapFactory
import android.os.Build
import android.os.CountDownTimer
import android.util.AttributeSet
import android.util.Log
import android.util.Size
import android.view.LayoutInflater
import android.view.View
import android.widget.FrameLayout
import androidx.camera.core.Camera
import androidx.camera.core.CameraSelector
import androidx.camera.core.ImageAnalysis
import androidx.camera.core.ImageCapture
import androidx.camera.core.ImageCaptureException
import androidx.camera.core.Preview
import androidx.camera.lifecycle.ProcessCameraProvider
import androidx.core.content.ContextCompat
import androidx.lifecycle.LifecycleOwner
import com.catazy.cameralib.databinding.ViewScanSurfaceBinding
import com.catazy.cameralib.ext.yuvToRgba
import com.catazy.cameralib.model.ScannerErrorModel
import com.catazy.cameralib.util.ImageDetectionProperties
import com.catazy.cameralib.util.OpenCvBridge
import org.opencv.android.Utils
import org.opencv.core.MatOfPoint2f
import org.opencv.core.Point
import java.io.File
import kotlin.math.max
import kotlin.math.min
import kotlin.math.roundToInt

/**
 * 作者: Tao
 * 日期: 2024/3/7
 * 时间: 14:26
 * 描述: 扫描界面
 */
class ScanSurfaceView @JvmOverloads constructor(
    context: Context,
    attrs: AttributeSet? = null,
    defStyle: Int = 0
) : FrameLayout(context, attrs, defStyle) {


    companion object {

        private const val TIME_POST_PICTURE = 1500L
        private const val DEFAULT_TIME_POST_PICTURE = 1500L
        private const val IMAGE_ANALYSIS_SCALE_WIDTH = 400
    }

    lateinit var lifecycleOwner: LifecycleOwner
    lateinit var listener: ScanSurfaceListener
    lateinit var originalImageFile: File
    private lateinit var mBinding: ViewScanSurfaceBinding

    //    private lateinit var mView:View
    private lateinit var previewSize: Size

    private val openCvBridge by lazy { OpenCvBridge() }
    private var cameraProvider: ProcessCameraProvider? = null
    private var camera: Camera? = null
    private var preview: Preview? = null

    // 自动捕获计时器
    private var autoCaptureTimer: CountDownTimer? = null

    private var millisLeft = 0L

    // 是否开启自动捕获
    private var isAutoCaptureOn: Boolean = true

    // 是否自动捕获已经计划
    private var isAutoCaptureScheduled = false

    // 是否正在捕获
    private var isCapturing = false

    // 拍照对象()
    private var imageCapture: ImageCapture? = null

    // 图像分析对象(获取缓冲区中的图片和视频帧数据)
    private var imageAnalysis: ImageAnalysis? = null

    // 闪光灯模式
    private var flashMode: Int = ImageCapture.FLASH_MODE_OFF

    init {
//        LayoutInflater.from(context).inflate(R.layout.view_scan_surface, this, true)
        mBinding = ViewScanSurfaceBinding.inflate(LayoutInflater.from(context), this, true)
    }

    /**
     * 开始去初始化相机
     */
    fun start() {
        mBinding.viewFinder.apply {
            post {
                measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED)
                previewSize = Size(width, height)
                openCamera()
            }
        }
    }

    /**
     * 解绑相机
     */
    fun unbindCamera() {
        cameraProvider?.unbind(imageAnalysis)
    }

    private fun clearAndInvalidateCanvas() {
        mBinding.scanCanvasView.clearShape()
    }

    /**
     * 打开相机
     */
    private fun openCamera() {
        val cameraProviderFuture = ProcessCameraProvider.getInstance(context)
        cameraProviderFuture.addListener({

            cameraProvider = cameraProviderFuture.get()
            try {
                bindCamera()
            } catch (e: Exception) {
                listener.onError(
                    ScannerErrorModel(
                        ScannerErrorModel.ErrorMessage.CAMERA_USE_CASE_BINDING_FAILED,
                        e
                    )
                )
            }

        }, ContextCompat.getMainExecutor(context))

    }

    private fun bindCamera() {
        cameraProvider?.unbindAll()
        camera = null
        setUseCase()
    }

    /**
     * 设置相机需要的用例
     */
    private fun setUseCase() {
        // 设置预览用例
        preview = Preview.Builder()
            .setTargetResolution(previewSize)
            .build()
            .also {
                it.setSurfaceProvider(mBinding.viewFinder.surfaceProvider)
            }
        // 设置拍照用例
        setImageCapture()

        // 设置图像分析用例
//        log("previewSize.width: ${previewSize.width} previewSize.height: ${previewSize.height}")
//        val aspectRatio = previewSize.width / previewSize.height.toFloat()
//        val width = IMAGE_ANALYSIS_SCALE_WIDTH
//        val height = (width / aspectRatio).roundToInt()
        imageAnalysis = ImageAnalysis.Builder()
            .setBackpressureStrategy(ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST)
            .setTargetResolution(previewSize)
            .build()
        imageAnalysis?.setAnalyzer(ContextCompat.getMainExecutor(context)) { image ->
            if (isAutoCaptureOn) {
                try {
                    log("预览图片的宽为: ${image.width} 高为: ${image.height}")
                    val mat = image.yuvToRgba()

                    // 转成Bitmap
                    val bitmap = Bitmap.createBitmap(
                        mat.width(),
                        mat.height(),
                        Bitmap.Config.ARGB_8888
                    )
                    Utils.matToBitmap(mat, bitmap)
                    listener.scanSurfacePictureTaken(bitmap)
                    log("图片的宽为: ${mat.width()} 高为: ${mat.height()}")

//                    val originalPreviewSize = mat.size()
//                    val largestQuad = openCvBridge.cannyTest(mat)
//                    listener.scanSurfacePictureTaken(bitmap)

                    mat.release()
//                    if (null != largestQuad) {
//                        log("获取到物体边框")
//                        drawLargestQuad(
//                            largestQuad.contour,
//                            largestQuad.points,
//                            originalPreviewSize
//                        )
////                        mBinding.scanCanvasView.showShape(
////                            originalPreviewSize.width.toFloat(),
////                            originalPreviewSize.height.toFloat(),
////                            largestQuad
////                        )
//                    } else {
//                        log("未获取到物体边框")
//                        clearAndInvalidateCanvas()
//                    }
                } catch (e: Exception) {
                    listener.onError(
                        ScannerErrorModel(
                            ScannerErrorModel.ErrorMessage.DETECT_LARGEST_QUADRILATERAL_FAILED,
                            e
                        )
                    )
                }
            } else {
                clearAndInvalidateCanvas()
            }
            image.close()
        }

        camera = cameraProvider?.bindToLifecycle(
            lifecycleOwner,
            CameraSelector.DEFAULT_BACK_CAMERA,
            preview,
            imageCapture,
            imageAnalysis
        )
    }

    /**
     * 设置拍照对象
     */
    private fun setImageCapture() {
        // 如果已经绑定了ImageCapture, 先解绑
        if (imageCapture != null && cameraProvider?.isBound(imageCapture!!) == true) {
            cameraProvider?.unbind(imageCapture!!)
        }
        imageCapture = null
        imageCapture = ImageCapture.Builder()
            .setFlashMode(flashMode)
            .build()
    }

    /**
     * 绘制最大的四边形
     */
    private fun drawLargestQuad(
        approx: MatOfPoint2f,
        points: Array<Point>,
        stdSize: org.opencv.core.Size
    ) {
        // Attention: axis are swapped
        val previewWidth = stdSize.height.toFloat()
        val previewHeight = stdSize.width.toFloat()

//        val resultWidth =
//            max(previewWidth - points[0].y.toFloat(), previewWidth - points[1].y.toFloat()) -
//                    min(previewWidth - points[2].y.toFloat(), previewWidth - points[3].y.toFloat())
//
//        val resultHeight = max(points[1].x.toFloat(), points[2].x.toFloat()) - min(
//            points[0].x.toFloat(),
//            points[3].x.toFloat()
//        )
//
//        val imgDetectionPropsObj = ImageDetectionProperties(
//            previewWidth.toDouble(), previewHeight.toDouble(),
//            points[0], points[1], points[2], points[3], resultWidth.toInt(), resultHeight.toInt()
//        )
//        if (imgDetectionPropsObj.isNotValidImage(approx)) {
//            log("Invalid Image")
//            mBinding.scanCanvasView.clearShape()
//            cancelAutoCapture()
//        } else {
//            log("Valid Image")
//            if (!isAutoCaptureScheduled) {
//                scheduleAutoCapture()
//            }
        mBinding.scanCanvasView.showShape(previewWidth, previewHeight, points)
//        }
    }

    private fun scheduleAutoCapture() {
        isAutoCaptureScheduled = true
        millisLeft = 0L
        autoCaptureTimer = object : CountDownTimer(DEFAULT_TIME_POST_PICTURE, 100) {
            override fun onTick(millisUntilFinished: Long) {
                if (millisUntilFinished != millisLeft) {
                    millisLeft = millisUntilFinished
                }
            }

            override fun onFinish() {
                isAutoCaptureScheduled = false
                autoCapture()
            }
        }
        autoCaptureTimer?.start()
    }

    private fun cancelAutoCapture() {
        if (isAutoCaptureScheduled) {
            isAutoCaptureScheduled = false
            autoCaptureTimer?.cancel()
        }
    }

    private fun autoCapture() {
        if (isCapturing)
            return
        cancelAutoCapture()
        takePicture()
    }

    fun takePicture() {
        log("ZDCtakePicture Starts ${System.currentTimeMillis()}")
        listener.scanSurfaceShowProgress()
        isCapturing = true

        val imageCapture = imageCapture ?: return
        val outputOptions = ImageCapture.OutputFileOptions.Builder(originalImageFile).build()

        imageCapture.takePicture(outputOptions, ContextCompat.getMainExecutor(context),
            object : ImageCapture.OnImageSavedCallback {
                override fun onError(exc: ImageCaptureException) {
                    listener.scanSurfaceHideProgress()
                    log("${ScannerErrorModel.ErrorMessage.PHOTO_CAPTURE_FAILED.error}: ${exc.message}")
                    listener.onError(
                        ScannerErrorModel(
                            ScannerErrorModel.ErrorMessage.PHOTO_CAPTURE_FAILED,
                            exc
                        )
                    )
                }

                override fun onImageSaved(output: ImageCapture.OutputFileResults) {
                    listener.scanSurfaceHideProgress()
                    unbindCamera()
                    clearAndInvalidateCanvas()
//                    listener.scanSurfacePictureTaken()
                    postDelayed({ isCapturing = false }, TIME_POST_PICTURE)
                    log("ZDCtakePicture ends ${System.currentTimeMillis()}")
                }
            })
    }

    private fun log(msg: String) {
        Log.d("ScanSurfaceView", msg)
    }
}